package com.grace.web.controller.cms;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.message.BasicNameValuePair;
import org.apache.velocity.shaded.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.grace.common.annotation.Log;
import com.grace.common.core.domain.AjaxResult;
import com.grace.common.core.page.TableDataInfo;
import com.grace.common.enums.BusinessType;
import com.grace.common.enums.OperateTitle;
import com.grace.common.utils.StringUtils;
import com.grace.system.controller.UserBaseController;
import com.grace.system.domain.SysConfig;
import com.grace.system.service.impl.SysConfigServiceImpl;
import com.grace.cms.admin.service.ICmsThemeService;
import com.grace.cms.admin.service.ICmsWebService;
import com.grace.cms.common.constant.OlySystemConstant;
import com.grace.cms.common.domain.entity.CmsTheme;
import com.grace.cms.common.domain.entity.CmsWeb;
import com.grace.cms.common.enums.ThemeEnabledEnums;
import com.grace.cms.common.enums.properties.OlyThemeConfigProperties;
import com.grace.cms.common.enums.properties.OlyWebConfigProperties;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

//统一主题管理入口
@RestController
@RequestMapping("/cms/theme")
public class CmsThemeController extends UserBaseController {

    @Autowired
    private ICmsThemeService cmsThemeService;

    @Autowired
    private ICmsWebService webService;

    @Autowired
    private SysConfigServiceImpl sysConfigService;

    @Autowired
    private RestTemplate restTemplate;

    private static final Logger logger = LoggerFactory.getLogger(CmsThemeController.class);

    /**
     * 查询主题列表
     * 
     * @param cmsTheme
     * @return
     */
    @PreAuthorize("@ss.hasPermi('cms:theme:list')")
    @GetMapping("/list")
    public TableDataInfo list(CmsTheme cmsTheme) {
        startPage();
        List<CmsTheme> list = cmsThemeService.listCmsTheme(cmsTheme);
        return getDataTable(list);
    }

    /**
     * 查询主题
     * 
     * @param webName   站点名
     * @param themeName 主题名
     * @return
     */
    @PreAuthorize("@ss.hasPermi('cms:theme:list')")
    @GetMapping("/getTheme/{webName}/{themeName}")
    public AjaxResult getTheme(@PathVariable("webName") String webName,
            @PathVariable("themeName") String themeName) {
        CmsTheme cmsTheme = new CmsTheme();
        cmsTheme.setWebName(webName);
        cmsTheme.setThemeName(themeName);
        return success(cmsThemeService.selectCmsThemeByWt(cmsTheme));
    }

    /**
     * 更新主题
     * 
     * @param cmsTheme
     * @return
     */
    @PreAuthorize("@ss.hasPermi('cms:theme:edit')")
    @Log(title = OperateTitle.CMS_THEME, businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody CmsTheme cmsTheme) {
        return toAjax(cmsThemeService.updateCmsTheme(cmsTheme));
    }

    /**
     * 
     * 获取主题名列表
     * 
     * @param webName
     * @return
     */
    @PreAuthorize("@ss.hasPermi('cms:theme:list')")
    @GetMapping("/listThemeNames/{webName}")
    public AjaxResult listThemeNames(@PathVariable("webName") String webName) {
        CmsTheme cmsTheme = new CmsTheme();
        cmsTheme.setWebName(webName);
        List<String> themeNames = cmsThemeService.listCmsTheme(cmsTheme).stream()
                .map(CmsTheme::getThemeName).collect(Collectors.toList());
        return success(themeNames);
    }

    /**
     * 修改保存参数配置 通过key批量保存 默认设置为字符串类型
     */

    @Log(title = OperateTitle.CMS_THEME, businessType = BusinessType.UPDATE)
    @PostMapping("/updateConfig/{configGroup}")
    @PreAuthorize("@ss.hasPermi('cms:theme:config')")
    public AjaxResult updateConfig(@RequestBody Map<String, Object> mp,
            @PathVariable("configGroup") String configGroup) {
        mp.put("configGroup", configGroup);
        int num = sysConfigService.updatesByMap(mp, getUsername());
        return toAjax(num);
    }

    /**
     * 上传主题
     * 
     * 
     * @param file    文件名
     * @param webName 服务名
     * @param cover   是否覆盖
     * @param request
     * @return
     * @throws Throwable
     */
    @PostMapping("/uploadTheme/{webName}")
    @PreAuthorize("@ss.hasPermi('cms:theme:install')")
    @Log(title = OperateTitle.CMS_THEME, businessType = BusinessType.UPDATE)
    public AjaxResult themeUpload(@RequestParam("themeFile") MultipartFile file,
            @PathVariable("webName") String webName,
            @RequestParam("cover") boolean cover,
            HttpServletRequest request) {
        String themeName = FilenameUtils.getBaseName(file.getOriginalFilename());

        CmsWeb cmsWeb = webService.selectCmsWebByWebName(webName);
        if (cmsWeb == null) {
            return error("站点不存在,请先插入站点再进行安装");
        }
        if (file.isEmpty() || !"zip|gz|rar".contains(FilenameUtils.getExtension(file.getOriginalFilename()))) {
            return error("文件不存在或者不支持压缩类型,请上传.zip|.gz|.rar压缩包");
        } else {
            Map<String, String> header = new HashMap<>(1);
            Map<String, String> params = new HashMap<>();
            params.put("fileType", "zip/gz/rar");
            params.put("cover", Boolean.toString(cover));
            header.put("Accept", "application/json");
            header.put("Accept-Charset", "UTF-8");
            AjaxResult aj = this.uploadFile(
                    cmsWeb.getWebUrl() + "/" + themeName + OlySystemConstant.THEME_MANAGE_URL_PREFIX + "/uploadTheme",
                    file,
                    "themeFile",
                    header,
                    params);
            if (aj.get(AjaxResult.CODE_TAG).equals(HttpStatus.OK.value())) {
                try {
                    return syncThemeInfo(webName, themeName, cover,
                            cmsWeb.getWebUrl());
                } catch (ClientProtocolException e) {

                } catch (IOException e) {

                }
            }
            return aj;
        }

    }

    /**
     * 删除主题
     * 
     * @param webName
     * @param themeName
     * @return
     * @throws IOException
     * @throws ClientProtocolException
     */
    @DeleteMapping("/remove/{webName}/{themeName}")
    @PreAuthorize("@ss.hasPermi('cms:theme:remove')")
    @Log(title = OperateTitle.CMS_THEME, businessType = BusinessType.DELETE)
    public AjaxResult removeTheme(@PathVariable("webName") String webName, @PathVariable("themeName") String themeName)
            throws ClientProtocolException {
        CmsWeb cmsWeb = webService.selectCmsWebByWebName(webName);
        if (cmsWeb == null) {
            return error("站点不存在!");
        }
        if (deleteThemeFile(
                cmsWeb.getWebUrl() + "/" + themeName + OlySystemConstant.THEME_MANAGE_URL_PREFIX + "/deleteTheme",
                themeName).get(AjaxResult.CODE_TAG)
                .equals(HttpStatus.OK.value())) {
            // 删除配置参数
            sysConfigService.deleteConfigByGroup(webName + "_" + themeName);
            CmsTheme cmsTheme = new CmsTheme();
            cmsTheme.setWebName(webName);
            cmsTheme.setThemeName(themeName);
            // 删除主题和主题关联的文章
            cmsThemeService.deleteCmsThemeByTn(cmsTheme);
            return success();
        }
        return error();
    }

    /**
     * 获取配置表单
     * 
     * @param webName
     * @param themeName
     * @return
     * @throws ClientProtocolException
     * @throws IOException
     */
    @GetMapping("/getThemeConfigForm/{webName}/{themeName}")
    @PreAuthorize("@ss.hasPermi('cms:theme:config')")
    public AjaxResult getThemeConfigForm(@PathVariable("webName") String webName,
            @PathVariable("themeName") String themeName)
            throws ClientProtocolException, IOException {
        CmsWeb cmsWeb = webService.selectCmsWebByWebName(webName);
        if (cmsWeb == null) {
            return error("站点不存在!");
        }
        return this.get(
                cmsWeb.getWebUrl() + "/" + themeName + OlySystemConstant.THEME_MANAGE_URL_PREFIX
                        + "/getThemeConfigForm");
    }

    // 更新索引文件
    @PostMapping("/updateSiteMapIndex/{webName}/{themeName}")
    @PreAuthorize("@ss.hasPermi('cms:theme:config')")
    public AjaxResult updateSiteMapIndex(@PathVariable("webName") String webName,
            @PathVariable("themeName") String themeName) throws ClientProtocolException {
        CmsWeb cmsWeb = webService.selectCmsWebByWebName(webName);
        if (cmsWeb == null) {
            return error("站点不存在!");
        }
        return this.post(
                cmsWeb.getWebUrl() + "/" + themeName + OlySystemConstant.THEME_MANAGE_URL_PREFIX + "/siteMapIndex",
                null);
    }

    /**
     * 同步主题数据
     * 
     * @param webName
     * @param themeName
     * @param cover
     * @param url
     * @return
     * @throws ClientProtocolException
     * @throws IOException
     */
    private AjaxResult syncThemeInfo(String webName, String themeName, boolean cover, String url)
            throws ClientProtocolException, IOException {
        CmsTheme searchTheme = new CmsTheme();
        searchTheme.setWebName(webName);
        searchTheme.setThemeName(themeName);
        CmsTheme s = cmsThemeService.selectCmsThemeByWt(searchTheme);
        if (s != null && !cover) {
            return AjaxResult.success("主题已上传,数据未进更新!");
        } else {
            try {
                // 获取配置Json
                JSONObject con = JSONObject
                        .parseObject(
                                this.get(url + "/" + themeName + OlySystemConstant.THEME_MANAGE_URL_PREFIX
                                        + "/getThemeSetting").get(AjaxResult.DATA_TAG)
                                        .toString());
                CmsTheme theme = JSONObject.parseObject(
                        JSON.toJSONString(
                                con.get(OlyThemeConfigProperties.THEME_DATA_PREFIX.defaultValue())),
                        CmsTheme.class);
                theme.setWebName(webName);
                theme.setCreateBy(getUsername());
                theme.setThemeName(themeName);
                if (s != null && cover) {
                    theme.setThemeEnabled(s.getThemeEnabled());
                    cmsThemeService.updateCmsTheme(theme);
                    addThemeConfig(JSON.parseArray(
                            JSON.toJSONString(
                                    con.get(OlyThemeConfigProperties.THEME_CONFIG_PREFIX.defaultValue())),
                            SysConfig.class), themeName, webName);
                    return AjaxResult.success("主题已覆盖上传,数据已经更新!");
                } else {
                    theme.setThemeEnabled(ThemeEnabledEnums.CLOSE.ordinal());
                    cmsThemeService.insertCmsTheme(theme);
                    addThemeConfig(JSON.parseArray(
                            JSON.toJSONString(
                                    con.get(OlyThemeConfigProperties.THEME_CONFIG_PREFIX.defaultValue())),
                            SysConfig.class), themeName, webName);
                    return AjaxResult.success("主题已上传,数据已同步!");
                }
            } catch (Exception e) {
                logger.info("主题上传异常,数据同步失败!" + e.getMessage());
                return AjaxResult.error("主题上传异常,数据同步失败!");
            }

        }

    }

    /**
     * 获取主题树
     * 
     * @param webName
     * @param themeName
     * @return
     * @throws IOException
     * @throws ClientProtocolException
     */
    @GetMapping("/getThemeTree/{webName}/{themeName}")
    @PreAuthorize("@ss.hasPermi('cms:theme:tree')")
    public AjaxResult themeTree(@PathVariable("webName") String webName, @PathVariable("themeName") String themeName)
            throws ClientProtocolException, IOException {
        CmsWeb cmsWeb = webService.selectCmsWebByWebName(webName);
        if (cmsWeb == null) {
            return error("站点不存在!");
        }
        return get(cmsWeb.getWebUrl() + "/" + themeName + OlySystemConstant.THEME_MANAGE_URL_PREFIX + "/getThemeTree");
    }

    /**
     * 获取文件内容
     * 
     * @param path
     * @return
     * @throws IOException
     * @throws ClientProtocolException
     */
    @GetMapping("/getThemeFileContent/{webName}/{themeName}")
    public AjaxResult themeContent(@PathVariable("webName") String webName, @PathVariable("themeName") String themeName,
            @RequestParam("path") String path) {
        if (isSupportSuffix(path)) {
            return AjaxResult.error("不支持文件类型！");
        }
        CmsWeb cmsWeb = webService.selectCmsWebByWebName(webName);
        if (cmsWeb == null) {
            return error("站点不存在!");
        }
        return this.get(
                cmsWeb.getWebUrl() + "/" + themeName + OlySystemConstant.THEME_MANAGE_URL_PREFIX
                        + "/getThemeFileContent?path=" + path);
    }

    @PostMapping("/updateThemeFileContent/{webName}/{themeName}")
    @PreAuthorize("@ss.hasPermi('cms:theme:update')")
    @Log(title = OperateTitle.CMS_THEME, businessType = BusinessType.UPDATE)
    public AjaxResult updateThemeFileContent(
            @PathVariable("webName") String webName,
            @PathVariable("themeName") String themeName,
            @RequestBody Map<String, String> paMap) {
        String path = paMap.get("path");
        String fileContent = paMap.get("fileContent");
        CmsWeb cmsWeb = webService.selectCmsWebByWebName(webName);
        if (cmsWeb == null) {
            return error("站点不存在!");
        } else if (isSupportSuffix(path)) {
            return AjaxResult.error("不支持文件类型！");
        } else {
            List<NameValuePair> params = new ArrayList<NameValuePair>();
            params.add(new BasicNameValuePair("fileContent", fileContent));
            params.add(new BasicNameValuePair("path", path));
            String url = cmsWeb.getWebUrl() + "/" + themeName + OlySystemConstant.THEME_MANAGE_URL_PREFIX
                    + "/updateThemeFileContent";
            return this.post(url, params);

        }

    }

    @PostMapping("/themeDownload/{webName}/{themeName}")
    public void themeDownload(@PathVariable("webName") String webName,
            @PathVariable("themeName") String themeName, HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        CmsWeb cmsWeb = webService.selectCmsWebByWebName(webName);
        String url = cmsWeb.getWebUrl() + "/" + themeName + OlySystemConstant.THEME_MANAGE_URL_PREFIX
                + "/themeDownload";
        clientDown(request, response, url, themeName);
    }

    /**
     * 同步主题
     * 
     * @throws IOException
     * @throws ClientProtocolException
     */
    @PostMapping("/syncTheme/{webName}")
    public AjaxResult syncTheme(@PathVariable("webName") String webName) throws ClientProtocolException, IOException {
        CmsWeb cmsWeb = webService.selectCmsWebByWebName(webName);
        CmsTheme cmsTheme = new CmsTheme();
        cmsTheme.setWebName(webName);
        if (cmsWeb == null) {
            return error("站点不存在!");
        }
        // 获取当前站点下已经安装的主题
        List<CmsTheme> listThemes = cmsThemeService.listCmsTheme(cmsTheme);
        List<String> upThemeNames = listThemes.stream().map(CmsTheme::getThemeName)
                .collect(Collectors.toList());
        // 获取已经在本地的主题
        List<String> localThemeNames = JSONArray.parseArray(
                this.get(cmsWeb.getWebUrl() + OlySystemConstant.THEME_MANAGE_URL_PREFIX + "/getThemeNames")
                        .get(AjaxResult.DATA_TAG).toString(),
                String.class);
        // 取差集
        Collection<String> syncThemeNames = CollectionUtils.subtract(localThemeNames, upThemeNames);
        if (syncThemeNames != null && syncThemeNames.size() > 0) {
            for (String themeName : syncThemeNames) {
                this.syncThemeInfo(webName, themeName, false, cmsWeb.getWebUrl());
            }
            return success("同步成功:" + syncThemeNames.toArray().toString());
        }
        return error("没有需要同步的主题!");

    }
    @PostMapping("/lucene/sync/{webName}/{themeName}")
    public AjaxResult luceneSync(@PathVariable("themeName") String themeName, @PathVariable("webName") String webName) {
        CmsWeb cmsWeb = webService.selectCmsWebByWebName(webName);
        String url = cmsWeb.getWebUrl() + "/" + themeName + OlySystemConstant.WEB_MANAGE_URL_PREFIX
                + "/lucene/sync";
        return post(url, null);
    }

    @PostMapping("/lucene/rebuild/{webName}/{themeName}")
    public AjaxResult luceneRebuild(@PathVariable("themeName") String themeName, @PathVariable("webName") String webName) {
        CmsWeb cmsWeb = webService.selectCmsWebByWebName(webName);
        String url = cmsWeb.getWebUrl() + "/" + themeName + OlySystemConstant.WEB_MANAGE_URL_PREFIX
                + "/lucene/rebuild";
        return post(url, null);
    }

    /**
     * 文件下载
     */
    public void clientDown(HttpServletRequest request, HttpServletResponse response,
            String urlPath, String webName) {
        OutputStream out = null;
        try {
            // 设置请求头
            HttpHeaders headers = new HttpHeaders();
            headers.set(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + webName + ".zip");

            // 创建 HttpEntity
            HttpEntity<Void> requestEntity = new HttpEntity<>(headers);

            // 发送请求并获取响应
            ResponseEntity<Resource> responseEntity = restTemplate.exchange(
                    urlPath,
                    HttpMethod.POST,
                    requestEntity,
                    Resource.class);

            // 检查响应状态码
            if (responseEntity.getStatusCode() != HttpStatus.OK) {
                logger.error("文件下载失败: {}", responseEntity.getStatusCode());
                return;
            }
            // 获取文件流
                Resource resource = responseEntity.getBody();
                if (resource == null) {
                    logger.error("响应体为空，文件下载失败");
                    return;
                }
             InputStream in = resource.getInputStream();

            // 设置响应头
            response.setCharacterEncoding("utf-8");
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + webName + ".zip");

            // 写入文件流
            out = response.getOutputStream();
            byte[] buffer = new byte[4096];
            int readLength;
            while ((readLength = in.read(buffer)) > 0) {
                out.write(buffer, 0, readLength);
            }
            out.flush();
        } catch (IOException e) {
            logger.error("文件下载 IO 异常: {}", e.getMessage());
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                logger.error("关闭输出流异常: {}", e.getMessage());
            }
        }
    }

    /**
     * 添加配置
     * 
     * @param listConfigs
     * @param themeName   主题名字
     * @param webName     站点名字
     */
    private void addThemeConfig(List<SysConfig> listConfigs, String themeName, String webName) {
        if (StringUtils.isNotEmpty(listConfigs)) {
            for (SysConfig sysConfig : listConfigs) {
                if (sysConfig.getConfigKey()
                        .startsWith(OlyWebConfigProperties.THEME_CONFIG_PREFIX.getValue() + themeName)) {
                    sysConfig.setConfigType("N");
                    sysConfig.setConfigGroup(webName + "_" + themeName);
                    if (sysConfigService.selectConfigByGk(sysConfig.getConfigGroup(),
                            sysConfig.getConfigKey()) != null) {
                        sysConfig.setUpdateBy("");
                        sysConfigService.updateConfigByGk(sysConfig);
                    } else {
                        sysConfig.setCreateBy("");
                        sysConfigService.insertConfig(sysConfig);
                    }
                }
            }
        }
    }

    /**
     * 删除远程主题
     * 
     * @param url
     * @param themeName
     * @return
     * @throws IOException
     * @throws ClientProtocolException
     */
    private AjaxResult deleteThemeFile(String url, String themeName)
            throws ClientProtocolException {
        return post(url, null);
    };

    /**
     * /**
     * 发送 POST 请求
     */
    private AjaxResult post(String url, List<NameValuePair> params) {
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
            if (params != null) {
                for (NameValuePair param : params) {
                    body.add(param.getName(), param.getValue());
                }
            }
            HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
            ResponseEntity<String> response = restTemplate.postForEntity(url, requestEntity, String.class);
            return JSONObject.parseObject(response.getBody(), AjaxResult.class);
        } catch (Exception e) {
            logger.error("POST 请求失败: {}", e.getMessage());
            return error("数据提交异常");
        }
    }

    /**
     * 发送 GET 请求
     */
    private AjaxResult get(String url) {
        try {
            String response = restTemplate.getForObject(url, String.class);
            return JSONObject.parseObject(response, AjaxResult.class);
        } catch (Exception e) {
            logger.error("GET 请求失败: {}", e.getMessage());
            return error("数据获取失败");
        }
    }

    /**
     * 文件上传
     */
    private AjaxResult uploadFile(String url, MultipartFile file, String fileParamName,
            Map<String, String> headerParams, Map<String, String> otherParams) {
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.MULTIPART_FORM_DATA);

            // 添加自定义 Header
            for (Map.Entry<String, String> entry : headerParams.entrySet()) {
                headers.add(entry.getKey(), entry.getValue());
            }

            // 构建请求体
            MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
            body.add(fileParamName, new ByteArrayResource(file.getBytes()) {
                @Override
                public String getFilename() {
                    return file.getOriginalFilename();
                }
            });

            for (Map.Entry<String, String> entry : otherParams.entrySet()) {
                body.add(entry.getKey(), entry.getValue());
            }

            HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
            ResponseEntity<String> response = restTemplate.postForEntity(url, requestEntity, String.class);

            return JSONObject.parseObject(response.getBody(), AjaxResult.class);
        } catch (Exception e) {
            logger.error("文件上传失败: {}", e.getMessage());
            return error("文件上传失败");
        }
    }

    /**
     * 判断文件后缀是否支持编辑
     * 
     * @param path
     * @return
     */
    private boolean isSupportSuffix(String path) {
        String supportTypes = sysConfigService.selectConfigDefaultValue(
                OlyThemeConfigProperties.THEME_CONFIG_GROUP.getValue(),
                OlyThemeConfigProperties.THEME_FILE_EDIT_SUPPORT);
        return !supportTypes.contains(FilenameUtils.getExtension(path));
    }

}
